from datetime import datetime, timezone
from typing import Annotated

from passlib.context import CryptContext
from pydantic import StringConstraints
from sqlmodel import Field, Relationship, SQLModel

from app.auth.models.permissions import GenericPermissions, PermissionUser
from app.user.models.role import Role
from app.user.models.user_role import UserRole

# 密码哈希上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


class UserBase(SQLModel):
    username: Annotated[
        str,
        StringConstraints(
            min_length=4,
            max_length=20,
            strip_whitespace=True,
            pattern=r"^[a-zA-Z0-9_]+$",
        ),
    ] = Field(..., nullable=False, unique=True)

    model_config = {
        "arbitrary_types_allowed": True,
    }


class UserCreate(UserBase):
    password: str = Field(
        ..., min_length=6, max_length=50, description="密码", nullable=False
    )


class UserLogin(UserCreate): ...


class UserPublic(UserBase):
    id: int
    email: str | None
    created_at: datetime


class User(UserBase, table=True):
    id: int = Field(..., primary_key=True, index=True)
    hashed_password: str = Field(..., max_length=200, nullable=False)
    email: Annotated[
        str,
        StringConstraints(
            min_length=5,
            max_length=100,
            strip_whitespace=True,
            pattern=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
        ),
    ] = Field(..., max_length=100, nullable=True, unique=True)
    is_active: bool = Field(default=True, nullable=False)
    is_deleted: bool = Field(default=False, nullable=False)
    is_admin: bool = Field(default=False, nullable=False)
    created_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc), nullable=False
    )
    updated_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        nullable=False,
        sa_column_kwargs={"onupdate": lambda: datetime.now(timezone.utc)},
    )

    # ============ 关联关系 ============
    # 添加lazy加载策略
    roles: list[Role] = Relationship(
        link_model=UserRole,
        sa_relationship_kwargs={
            "lazy": "selectin",
            "backref": "users",
            "cascade": "save-update, merge",
        },
    )
    directed_permissions: list[GenericPermissions] = Relationship(
        link_model=PermissionUser,
        sa_relationship_kwargs={
            "lazy": "selectin",
            "backref": "directed_users",
            "cascade": "save-update, merge",
        },
    )

    def __hash__(self):
        return hash(self.id)

    # ============ 基础方法 ============

    @property
    def permissions(self):
        # 展开嵌套列表
        role_permissions = []
        for role in self.roles:
            role_permissions.extend(role.permissions)

        # 确保 self.directed_permissions 存在
        direct_permissions = getattr(self, "directed_permissions", [])

        # 合并并去重
        all_permissions = role_permissions + direct_permissions

        return list(set(all_permissions))

    # ============ 密码安全 ============
    @staticmethod
    def hash_password(password: str) -> str:
        """生成密码哈希"""
        return pwd_context.hash(password)

    def verify_password(self, password: str) -> bool:
        """验证密码"""
        return pwd_context.verify(password, str(self.hashed_password))

    # ============ 业务方法 ============
    def activate(self):
        """激活账户"""
        self.is_active = True

    def deactivate(self):
        """禁用账户"""
        self.is_active = False

    def add_role(self, role):
        """添加角色"""
        if role not in self.roles:
            self.roles.append(role)

    # ============ 序列化方法 ============
    def to_dict(self, include_sensitive=False):
        """转换为字典（用于API响应）"""
        data = {
            "id": self.id,
            "username": self.username,
            "email": self.email,
            "is_active": self.is_active,
            "is_deleted": self.is_deleted,
            "created_at": self.created_at.isoformat(),
        }
        if include_sensitive:
            data.update({"hashed_password": self.hashed_password})
        return data
